package mint.modules.generator.expression;

import java.util.ArrayList;
import java.util.Formatter;
import java.util.List;

import org.antlr.runtime.tree.Tree;

import mint.MintLexer;
import mint.modules.generator.CounterManager;
import mint.modules.generator.Scope;
import mint.modules.type.Type;

public class UnaryExprNotPlusMinus extends Expression {
	private Expression subExpr, auxSubExpr;
	private String sign;
	private List<String> errors;
	private int line;
	private Type type;
	private List<Selector> selectors;
	private Selector auxSelector;

	public UnaryExprNotPlusMinus(Scope theParent, ExpressionType exprType, Tree unaryExprNotPlusMinusTree) {
		super(theParent, exprType);		
		errors = new ArrayList<String>();
		line = unaryExprNotPlusMinusTree.getLine();
		type = null;
		auxSubExpr = null;
		selectors = null;		
		
		if (unaryExprNotPlusMinusTree.getText().equals("~"))
		{
			sign = "~";
			subExpr = new UnaryExprNode(theParent, exprType, unaryExprNotPlusMinusTree.getChild(0));		
		}
		else if (unaryExprNotPlusMinusTree.getText().equals("!"))
		{
			sign = "!";
			subExpr = new UnaryExprNode(theParent, exprType, unaryExprNotPlusMinusTree.getChild(0));
		}
		else if (unaryExprNotPlusMinusTree.getType() == MintLexer.CAST)
		{
			sign = null;
			subExpr = new CastExprNode(theParent, exprType, unaryExprNotPlusMinusTree);
		} else if (unaryExprNotPlusMinusTree.getType() == MintLexer.POSTINCREMENT)
		{
			sign = "++";
			subExpr = new PrimaryNode(theParent, exprType, unaryExprNotPlusMinusTree.getChild(0));
			auxSubExpr = new PrimaryNode(theParent, ExpressionType.Left, unaryExprNotPlusMinusTree.getChild(0));
		} else if (unaryExprNotPlusMinusTree.getType() == MintLexer.POSTDECREMENT)
		{
			sign = "--";
			subExpr = new PrimaryNode(theParent, exprType, unaryExprNotPlusMinusTree.getChild(0));
			auxSubExpr = new PrimaryNode(theParent, ExpressionType.Left, unaryExprNotPlusMinusTree.getChild(0));
		} else
		{
			sign = null;
			
			if (unaryExprNotPlusMinusTree.getChildCount() <= 1)
			{
				subExpr = new PrimaryNode(theParent, exprType, unaryExprNotPlusMinusTree.getChild(0));				
			} else
			{
				selectors = new ArrayList<Selector>();
				subExpr = new PrimaryNode(theParent, ExpressionType.Right, unaryExprNotPlusMinusTree.getChild(0));
				
				for (int i = 1; i < unaryExprNotPlusMinusTree.getChildCount() - 1; i++)
				{
					selectors.add(new Selector(theParent, ExpressionType.Right, unaryExprNotPlusMinusTree.getChild(i)));
				}
				
				selectors.add(new Selector(theParent, exprType, unaryExprNotPlusMinusTree.getChild(unaryExprNotPlusMinusTree.getChildCount() - 1)));
				auxSelector = new Selector(theParent, ExpressionType.Right, unaryExprNotPlusMinusTree.getChild(unaryExprNotPlusMinusTree.getChildCount() - 1));
			}
		}
	}

	@Override
	public void testSemantic() {
		subExpr.testSemantic();
		type = subExpr.getType();
		
		if (sign != null)
		{
			if (getExpressionType() != ExpressionType.Right)
				errors.add(line + ": " + sign + " must be on the right side of a operation");
			
			if (sign.equals("~"))
			{
				if (!subExpr.getType().equals(Type.intType))
					errors.add(line + ": The bitwise not operator must be used on integers");
				type = Type.intType;
			} else if (sign.equals("!"))
			{
				if (!subExpr.getType().equals(Type.booleanType))
					errors.add(line + ": The not operator must be used on booleans");
				type = Type.booleanType;
			} else if (sign.equals("++"))
			{
				if (!subExpr.getType().equals(Type.intType))
					errors.add(line + ": The postincrement operator must be used on integers");
				auxSubExpr.testSemantic();
				type = Type.intType;
			} else if (sign.equals("--"))
			{
				if (!subExpr.getType().equals(Type.intType))
				{
					errors.add(line + ": The postdecrement operator must be used on integers");
				}
				auxSubExpr.testSemantic();
				type = Type.intType;
			}			
		}
		
		if (selectors != null)
		{
			for (int i = 0; i < selectors.size(); i++)
			{
				selectors.get(i).testSemantic(type);
				
				if (i == selectors.size() - 1)
					auxSelector.testSemantic(type);
				
				type = selectors.get(i).getType();				
			}
			
		}

	}

	@Override
	public List<String> getSemanticErrors() {
		errors.addAll(subExpr.getSemanticErrors());
		return errors;
	}

	@Override
	public void emit(Formatter output) {
		if (sign != null)
		{
			if (sign.equals("~"))
			{
				subExpr.emit(output);
				output.format("iconst_m1\n");
				output.format("ixor\n");
			} else if (sign.equals("!"))
			{
				String label1 = CounterManager.getInstance().generate("not");
				String label2 = CounterManager.getInstance().generate("not");
				
				subExpr.emit(output);
				// Pinche not ¬.¬
				output.format("ifeq %s\n", label1);
				output.format("iconst_0\n");
				output.format("goto %s\n", label2);
				output.format("%s:\n", label1);
				output.format("iconst_1\n");
				output.format("%s:\n", label2);				
			} else if (sign.equals("++"))
			{
				subExpr.emit(output);
				auxSubExpr.emit(output);
				output.format("dup\n");
				output.format("iconst_1\n");
				output.format("iadd\n");
				auxSubExpr.leftEmit(output);
			} else if (sign.equals("--"))
			{
				subExpr.emit(output);
				output.format("dup\n");
				auxSubExpr.emit(output);
				output.format("iconst_1\n");
				output.format("isub\n");
				auxSubExpr.leftEmit(output);
			}
		}
		else
		{
			subExpr.emit(output);
			
			if (selectors != null)
			{
				for (Selector selec : selectors)
					selec.emit(output);
			}
		}
	}

	@Override
	public int getLine() {
		return line;
	}

	@Override
	public Type getType() {
		return type;
	}

	@Override
	public void leftEmit(Formatter output) {
		subExpr.leftEmit(output);
		
		if (selectors != null)
		{
			if (getExpressionType() == ExpressionType.Right)
			{}
			else if (getExpressionType() == ExpressionType.Left)
			{
				selectors.get(selectors.size() - 1).leftEmit(output);
			} else
			{
				selectors.get(selectors.size() - 1).leftEmit(output);
				
				subExpr.emit(output);
				
				if (selectors != null)
				{
					for (Selector selec : selectors)
						selec.emit(output);
				}
				auxSelector.emit(output);
			}
		}
	}

}
